package com.jeasonzhao.report.engine.impl;

import java.io.File;
import java.io.FilenameFilter;
import java.sql.SQLException;

import com.jeasonzhao.commons.basic.StringPairCollection;
import com.jeasonzhao.commons.db.helper.ConnectionStringHelper;
import com.jeasonzhao.commons.db.helper.ConnectionStringInfo;
import com.jeasonzhao.commons.logger.Logger;
import com.jeasonzhao.commons.utils.Algorithms;
import com.jeasonzhao.commons.utils.ConvertEx;
import com.jeasonzhao.commons.utils.ResourceException;
import com.jeasonzhao.commons.utils.ResourceHelper;
import com.jeasonzhao.commons.xml.XMLException;
import com.jeasonzhao.commons.xml.XMLHelper;
import com.jeasonzhao.commons.xml.XMLNode;
import com.jeasonzhao.model.ModelException;
import com.jeasonzhao.model.ModelHelper;
import com.jeasonzhao.report.codes.ConfigSrcTypes;
import com.jeasonzhao.report.engine.Configuration;
import com.jeasonzhao.report.engine.repo.DatabaseConfigurationRepository;
import com.jeasonzhao.report.engine.repo.DictRepository;
import com.jeasonzhao.report.engine.repo.ExceptionRepository;
import com.jeasonzhao.report.engine.repo.ReportRepository;
import com.jeasonzhao.report.exceptions.DatabaseException;
import com.jeasonzhao.report.exceptions.DictException;
import com.jeasonzhao.report.model.Report;
import com.jeasonzhao.report.model.ReportDatabaseConfiguration;
import com.jeasonzhao.report.model.ReportDict;
import com.jeasonzhao.report.exceptions.ReportLoaderException;

public class XmlFileResourceLoader extends AbstractLoader
{

    public XmlFileResourceLoader()
    {
    }

    public static final ReportDatabaseConfiguration initReportDatabaseFromXML(XMLNode nodeDatabase)
        throws DatabaseException
    {
        if(null == nodeDatabase)
        {
            return null;
        }
        ReportDatabaseConfiguration db = new ReportDatabaseConfiguration();
        db.setDatabaseId(nodeDatabase.subItemOrValueOf("id"));
        db.setDatabaseName(nodeDatabase.subItemOrValueOf("name","title"));
        String strClassName = nodeDatabase.subItemOrValueOf("class","provider","interface","classname");
        StringPairCollection params = new StringPairCollection();
        for(XMLNode nk : nodeDatabase.selectNodes("key","setting","settings"))
        {
            params.add(nk.valueOf("name","key","id"),nk.valueOf("value","data"));
        }
        db.setAppendParams(params);
        if(null != strClassName)
        {
            try
            {
                Class<?> cls = Class.forName(strClassName.trim());
                if(com.jeasonzhao.report.engine.irp.IConnectionProvider.class.isAssignableFrom(cls) == false)
                {
                    throw new DatabaseException.CastError(db,strClassName,com.jeasonzhao.report.engine.irp.IConnectionProvider.class);
                }
                db.setClassName(strClassName);
            }
            catch(ClassNotFoundException ex)
            {
                throw new DatabaseException.ClassNotFound(db,strClassName,ex);
            }
        } //null!=strClassName
        else
        {
            db.setUserName(nodeDatabase.subItemOrValueOf("username","user","usr"));
            db.setPassword(nodeDatabase.subItemOrValueOf("password","pwd"));
            String dbmsName = nodeDatabase.subItemOrValueOf("dbms","dbmsName","databaseName");
            db.setUrl(nodeDatabase.subItemOrValueOf("url","connectionString"));
            if(null != dbmsName)
            {
                try
                {
                    ConnectionStringInfo pair = ConnectionStringHelper.getInstance().getConnectionInfo(dbmsName,params.toStringArray());
                    if(null != pair)
                    {
                        db.setDriver(pair.getDriver());
                        db.setUrl(pair.getURL());
                    }
                }
                catch(SQLException ex)
                {
                    throw new DatabaseException.URLBuildException(db,ex);
                }
            }
            else
            {
                db.setDriver(nodeDatabase.subItemOrValueOf("driver","jdbcdriver"));
            }
            db.setPoolCapcity(ConvertEx.toInt(nodeDatabase.subItemOrValueOf("maxActive","poolsize","max"), -1));
            db.setPoolActive(ConvertEx.toInt(nodeDatabase.subItemOrValueOf("initialSize","init"), -1));
            String strURL = db.getUrl();
            if(null != strURL)
            {
                String rootFolder = ResourceHelper.getClassLocationDirectory(com.jeasonzhao.report.engine.Configuration.class);
                rootFolder = rootFolder.replaceAll("\\\\","/");
//                System.out.println("ROOT="+rootFolder);
//                System.out.println("OR                               " + strURL);
                String strRU = strURL.replaceAll("%CLASSES%",rootFolder);
//                System.out.println(">>                               " + strRU);
                db.setUrl(strRU);

            }
            if(null == db.getDriver())
            {
                throw new DatabaseException.NoDriver(db);
            }
            if(null == db.getUrl())
            {
                throw new DatabaseException.NoURL(db);
            }
            if(null == db.getDatabaseName())
            {
                db.setDatabaseName(db.getDatabaseId());
            }
        }
        return db;
    }

    public void refresh(
        Logger log
        ,DatabaseConfigurationRepository dbManager
        ,DictRepository dictManager
        ,ReportRepository reportManager
        ,ExceptionRepository exceptionManager)
        throws ReportLoaderException
    {
        String strConfigFileName = this.getProperties("file","folder","path","directory","from","files","folders","directories");
        if(Algorithms.isEmpty(strConfigFileName))
        {
            throw new ReportLoaderException.ResourceHasnotBeenConfigured(this.getClass(),"File/Folder Name");
        }
        File rootFile = ResourceHelper.file(Configuration.class,strConfigFileName);
        if(null==rootFile||rootFile.exists() == false)
        {
            throw new ReportLoaderException.ResourceHasnotBeenFound(this.getClass(),strConfigFileName);
        }
        File[] resourceFiles = rootFile.isDirectory()
            ? rootFile.listFiles(new ConfigFilenameFilter())
            : new File[]{rootFile};
        if(null == resourceFiles || resourceFiles.length < 1)
        {
            throw new ReportLoaderException.ResourceHasnotBeenFound(this.getClass(),strConfigFileName);
        }
        for(File file : resourceFiles)
        {
            try
            {
                XMLNode rootXmlNode = XMLHelper.fromResource(file.getAbsolutePath());
                for(XMLNode databaseNode : rootXmlNode.selectNodes("database"))
                {
                    try
                    {
                        ReportDatabaseConfiguration db = initReportDatabaseFromXML(databaseNode);
                        db.setConfigSrc(file.getAbsolutePath());
                        db.setConfigType(ConfigSrcTypes.Directory);
                        dbManager.add(db);
                    }
                    catch(DatabaseException ex1)
                    {
                        exceptionManager.addDatabaseConfigurationException(databaseNode.subItemAndValue("id"),ex1);
                        log.error("Load database from " + file.getAbsolutePath() + " Error ",ex1);
                    }
                }
                for(XMLNode dictNode : rootXmlNode.selectNodes("dict"))
                {
                    ReportDict dict = null;
                    try
                    {
                        dict = ModelHelper.getInstance().fromXML(ReportDict.class,dictNode);
                        if(null!=dict)
                        {
                        	dict.setConfigSrc(file.getAbsolutePath());
                        	dict.setConfigType(ConfigSrcTypes.Directory);
                        	dictManager.addDictionaryConfiguration(dict);
                        }
                    }
                    catch(DictException ex2)
                    {
                        exceptionManager.addDictConfigurationException(dict.getId(),ex2);
                        log.error("Load dict from " + file.getAbsolutePath() + " Error ",ex2);
                    }
                    catch(ModelException ex2)
                    {
                        exceptionManager.addDictConfigurationException(dictNode.subItemOrValueOf("id","key"),ex2);
                        log.error("Load dict from " + file.getAbsolutePath() + " Error ",ex2);
                    }
                }
                for(XMLNode reportNode : rootXmlNode.selectNodes("report"))
                {
                    try
                    {
                        Report report = ModelHelper.getInstance().fromXML(Report.class,reportNode);
                        report.setConfigSrc(file.getAbsolutePath());
                        report.setConfigType(ConfigSrcTypes.Directory);
                        reportManager.add(report);
                    }
                    catch(ModelException ex3)
                    {
                        exceptionManager.addReportException(reportNode.subItemOrValueOf("id","reportid"),ex3);
                        log.error("Load report from " + file.getAbsolutePath() + " Error ",ex3);
                    }
                }
            }
            catch(ResourceException ex2)
            {
                log.error("load file " + file.getAbsolutePath() + " error",ex2);
            }
            catch(XMLException ex2)
            {
                log.error("load XML " + file.getAbsolutePath() + " error",ex2);
            }
        }
    }

    public static final class ConfigFilenameFilter implements FilenameFilter
    {
        public boolean accept(File folder,String fileName)
        {
            if(null == folder || null == fileName || folder.exists() == false)
            {
                return false;
            }
            String name = fileName.toLowerCase();
            return name.endsWith(".xml")
                || name.endsWith(".config")
                || name.endsWith(".sqlreport")
                || name.endsWith(".report")
                || name.endsWith(".rpt")
                ;
        }
    }

}
